<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>大文件上传示例</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        .upload-container {
            border: 2px dashed #ccc;
            border-radius: 10px;
            padding: 40px;
            text-align: center;
            margin-bottom: 20px;
        }
        .upload-container.dragover {
            border-color: #007bff;
            background-color: #f8f9fa;
        }
        .progress-container {
            margin: 20px 0;
        }
        .progress-bar {
            width: 100%;
            height: 20px;
            background-color: #f0f0f0;
            border-radius: 10px;
            overflow: hidden;
        }
        .progress-fill {
            height: 100%;
            background-color: #007bff;
            transition: width 0.3s ease;
        }
        .file-info {
            margin: 10px 0;
            padding: 10px;
            background-color: #f8f9fa;
            border-radius: 5px;
        }
        .btn {
            padding: 10px 20px;
            margin: 5px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
        .btn-primary {
            background-color: #007bff;
            color: white;
        }
        .btn-danger {
            background-color: #dc3545;
            color: white;
        }
        .btn:disabled {
            opacity: 0.6;
            cursor: not-allowed;
        }
        .log {
            height: 200px;
            overflow-y: auto;
            border: 1px solid #ccc;
            padding: 10px;
            background-color: #f8f9fa;
            font-family: monospace;
            font-size: 12px;
        }
    </style>
</head>
<body>
    <h1>大文件上传示例</h1>
    <p>支持断点续传的大文件上传，适用于1-2GB的视频文件</p>

    <div class="upload-container" id="uploadContainer">
        <p>拖拽文件到此处或点击选择文件</p>
        <input type="file" id="fileInput" style="display: none;" accept="video/*">
        <button class="btn btn-primary" onclick="document.getElementById('fileInput').click()">选择文件</button>
    </div>

    <div id="fileInfo" class="file-info" style="display: none;">
        <h3>文件信息</h3>
        <p><strong>文件名:</strong> <span id="fileName"></span></p>
        <p><strong>文件大小:</strong> <span id="fileSize"></span></p>
        <p><strong>文件类型:</strong> <span id="fileType"></span></p>
        <p><strong>分片数量:</strong> <span id="chunkCount"></span></p>
    </div>

    <div class="progress-container" id="progressContainer" style="display: none;">
        <h3>上传进度</h3>
        <div class="progress-bar">
            <div class="progress-fill" id="progressFill" style="width: 0%"></div>
        </div>
        <p><span id="progressText">0%</span> - <span id="speedText">0 MB/s</span></p>
        <p>已上传: <span id="uploadedChunks">0</span> / <span id="totalChunks">0</span> 分片</p>
    </div>

    <div style="text-align: center; margin: 20px 0;">
        <button class="btn btn-primary" id="uploadBtn" onclick="startUpload()" disabled>开始上传</button>
        <button class="btn btn-primary" id="pauseBtn" onclick="pauseUpload()" disabled>暂停上传</button>
        <button class="btn btn-primary" id="resumeBtn" onclick="resumeUpload()" disabled>继续上传</button>
        <button class="btn btn-danger" id="cancelBtn" onclick="cancelUpload()" disabled>取消上传</button>
    </div>

    <div>
        <h3>上传日志</h3>
        <div class="log" id="logContainer"></div>
    </div>

    <script>
        // 配置参数
        const CONFIG = {
            baseUrl: '/api/admin/bigfile',  // 根据你的实际API地址修改
            chunkSize: 5 * 1024 * 1024,    // 5MB分片大小
            model: 'video',                 // 模块名称
            pid: 9                          // 分类ID，9表示视频文件
        };

        // 全局变量
        let selectedFile = null;
        let fileIdentifier = null;
        let totalChunks = 0;
        let uploadedChunks = 0;
        let isUploading = false;
        let isPaused = false;
        let uploadStartTime = 0;
        let uploadedBytes = 0;

        // 初始化
        document.addEventListener('DOMContentLoaded', function() {
            const uploadContainer = document.getElementById('uploadContainer');
            const fileInput = document.getElementById('fileInput');

            // 拖拽上传
            uploadContainer.addEventListener('dragover', function(e) {
                e.preventDefault();
                uploadContainer.classList.add('dragover');
            });

            uploadContainer.addEventListener('dragleave', function(e) {
                e.preventDefault();
                uploadContainer.classList.remove('dragover');
            });

            uploadContainer.addEventListener('drop', function(e) {
                e.preventDefault();
                uploadContainer.classList.remove('dragover');
                const files = e.dataTransfer.files;
                if (files.length > 0) {
                    handleFileSelect(files[0]);
                }
            });

            // 文件选择
            fileInput.addEventListener('change', function(e) {
                if (e.target.files.length > 0) {
                    handleFileSelect(e.target.files[0]);
                }
            });
        });

        // 处理文件选择
        function handleFileSelect(file) {
            selectedFile = file;
            fileIdentifier = generateFileIdentifier(file);
            totalChunks = Math.ceil(file.size / CONFIG.chunkSize);

            // 显示文件信息
            document.getElementById('fileName').textContent = file.name;
            document.getElementById('fileSize').textContent = formatFileSize(file.size);
            document.getElementById('fileType').textContent = file.type;
            document.getElementById('chunkCount').textContent = totalChunks;
            document.getElementById('fileInfo').style.display = 'block';

            // 启用上传按钮
            document.getElementById('uploadBtn').disabled = false;

            // 检查是否有断点续传
            checkFileExists();

            log(`文件选择完成: ${file.name} (${formatFileSize(file.size)})`);
        }

        // 生成文件唯一标识符
        function generateFileIdentifier(file) {
            return `${file.name}_${file.size}_${file.lastModified}`.replace(/[^a-zA-Z0-9]/g, '_');
        }

        // 检查文件是否已存在（断点续传）
        async function checkFileExists() {
            try {
                const response = await fetch(`${CONFIG.baseUrl}/check?identifier=${fileIdentifier}`);
                const result = await response.json();
                
                if (result.code === 200 && result.data.uploadedChunks > 0) {
                    uploadedChunks = result.data.uploadedChunks;
                    updateProgress();
                    log(`发现断点续传记录，已上传 ${uploadedChunks}/${totalChunks} 分片`);
                }
            } catch (error) {
                log(`检查断点续传失败: ${error.message}`);
            }
        }

        // 开始上传
        async function startUpload() {
            if (!selectedFile) {
                alert('请先选择文件');
                return;
            }

            isUploading = true;
            isPaused = false;
            uploadStartTime = Date.now();
            uploadedBytes = uploadedChunks * CONFIG.chunkSize;

            document.getElementById('uploadBtn').disabled = true;
            document.getElementById('pauseBtn').disabled = false;
            document.getElementById('cancelBtn').disabled = false;
            document.getElementById('progressContainer').style.display = 'block';

            log('开始上传...');

            try {
                await uploadChunks();
                if (!isPaused && uploadedChunks === totalChunks) {
                    await mergeChunks();
                }
            } catch (error) {
                log(`上传失败: ${error.message}`);
                resetUploadState();
            }
        }

        // 上传分片
        async function uploadChunks() {
            for (let i = uploadedChunks; i < totalChunks && isUploading && !isPaused; i++) {
                const start = i * CONFIG.chunkSize;
                const end = Math.min(start + CONFIG.chunkSize, selectedFile.size);
                const chunk = selectedFile.slice(start, end);

                const formData = new FormData();
                formData.append('file', chunk);
                formData.append('identifier', fileIdentifier);
                formData.append('filename', selectedFile.name);
                formData.append('chunkNumber', i);
                formData.append('chunkSize', CONFIG.chunkSize);
                formData.append('currentChunkSize', chunk.size);
                formData.append('totalSize', selectedFile.size);
                formData.append('totalChunks', totalChunks);
                formData.append('type', selectedFile.type);
                formData.append('model', CONFIG.model);
                formData.append('pid', CONFIG.pid);

                try {
                    const response = await fetch(`${CONFIG.baseUrl}/chunk`, {
                        method: 'POST',
                        body: formData
                    });

                    const result = await response.json();
                    if (result.code === 200) {
                        uploadedChunks = i + 1;
                        uploadedBytes += chunk.size;
                        updateProgress();
                        log(`分片 ${i + 1}/${totalChunks} 上传成功`);
                    } else {
                        throw new Error(result.msg || '上传失败');
                    }
                } catch (error) {
                    log(`分片 ${i + 1} 上传失败: ${error.message}`);
                    throw error;
                }
            }
        }

        // 合并分片
        async function mergeChunks() {
            log('开始合并文件...');

            const params = new URLSearchParams({
                identifier: fileIdentifier,
                filename: selectedFile.name,
                totalChunks: totalChunks,
                totalSize: selectedFile.size,
                type: selectedFile.type,
                model: CONFIG.model,
                pid: CONFIG.pid
            });

            try {
                const response = await fetch(`${CONFIG.baseUrl}/merge`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    body: params
                });

                const result = await response.json();
                if (result.code === 200) {
                    log('文件上传完成！');
                    log(`文件URL: ${result.data.fileResult.url}`);
                    alert('文件上传成功！');
                    resetUploadState();
                } else {
                    throw new Error(result.msg || '合并失败');
                }
            } catch (error) {
                log(`文件合并失败: ${error.message}`);
                throw error;
            }
        }

        // 暂停上传
        function pauseUpload() {
            isPaused = true;
            isUploading = false;
            document.getElementById('pauseBtn').disabled = true;
            document.getElementById('resumeBtn').disabled = false;
            log('上传已暂停');
        }

        // 继续上传
        function resumeUpload() {
            isPaused = false;
            document.getElementById('pauseBtn').disabled = false;
            document.getElementById('resumeBtn').disabled = true;
            log('继续上传...');
            startUpload();
        }

        // 取消上传
        async function cancelUpload() {
            isUploading = false;
            isPaused = false;

            try {
                const response = await fetch(`${CONFIG.baseUrl}/cancel?identifier=${fileIdentifier}`, {
                    method: 'DELETE'
                });
                log('上传已取消');
            } catch (error) {
                log(`取消上传失败: ${error.message}`);
            }

            resetUploadState();
        }

        // 重置上传状态
        function resetUploadState() {
            isUploading = false;
            isPaused = false;
            uploadedChunks = 0;
            uploadedBytes = 0;

            document.getElementById('uploadBtn').disabled = false;
            document.getElementById('pauseBtn').disabled = true;
            document.getElementById('resumeBtn').disabled = true;
            document.getElementById('cancelBtn').disabled = true;

            updateProgress();
        }

        // 更新进度
        function updateProgress() {
            const progress = totalChunks > 0 ? (uploadedChunks / totalChunks) * 100 : 0;
            document.getElementById('progressFill').style.width = `${progress}%`;
            document.getElementById('progressText').textContent = `${progress.toFixed(1)}%`;
            document.getElementById('uploadedChunks').textContent = uploadedChunks;
            document.getElementById('totalChunks').textContent = totalChunks;

            // 计算上传速度
            if (uploadStartTime > 0) {
                const elapsed = (Date.now() - uploadStartTime) / 1000;
                const speed = uploadedBytes / elapsed;
                document.getElementById('speedText').textContent = `${formatFileSize(speed)}/s`;
            }
        }

        // 格式化文件大小
        function formatFileSize(bytes) {
            if (bytes === 0) return '0 Bytes';
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }

        // 日志输出
        function log(message) {
            const logContainer = document.getElementById('logContainer');
            const time = new Date().toLocaleTimeString();
            logContainer.innerHTML += `[${time}] ${message}\n`;
            logContainer.scrollTop = logContainer.scrollHeight;
        }
    </script>
</body>
</html> 